# # -*- coding: utf-8 -*-

# 面向对象的三大特性 
# 封装  继承  多态
# 封装  函数是对语句的封装，类书籍对函数和变量的封装
# 继承  类与类之间的父子关系
# 多态  是一种技巧提高灵活性


# # 小明14 17.8m高 早上跑步 回去吃东西
# # 小李13 15.8m高 不跑步  喜欢吃东西
# # print('123')


# class Student(object):  # 关注类有哪些属性和行为
#     # __slots__ = ('name','age','height') #規定可以存在的屬性

#     def __init__(self, name, age, height):  # 在init以参数定义特征，我们称为属性
#         self.name = name
#         self.age = age
#         self.height = height

#     def run(self):
#         print('正在跑步')

#     def eat(self):
#         print('正在吃东西')

#     def call(self):
#         print('大家好我是', self)

# # 定义两个实例对象  并赋值__init__方法
# # 实现为
# # 1：调用__new__ 方法申请内存空间
# # 2：调用__init__ 方法并让self指向申请好的内存空间 填充数据
# # 3：变量s1 也指向创建好的内存空间


# # s1 = Student('小明', '14', '17.8')
# # s2 = Student('小李', '13', '15.8')
# # s1.call()

# # # 动态属性没有定义 往里面加
# # Student.city = '北京'
# # print(s2.city)

# # # 根据不同的业务逻辑执行不同操作
# # s1.run()
# # s1.eat()

# # s2.eat()

# # # 算数相关魔法方法
# # class Person():
# #     def __init__(self, name, age):
# #         self.name = name
# #         self.age = age

# #     #__eq__方法 == 如果不写就是比较内存地址
# #     def __eq__(self, other):
# #         return self.name == other.name and self.age == other.age
# #     def __gt__(self,other):
# #         pass

# # #!=  本质为__ne__(not eq)  或者__eq__方法去反


# # p1 = Person('xiaowang', 18)
# # p2 = Person('xiaowang', 18)
# # p3 = Person('xiaowan', 18)
# # print(p1 is p2)
# # print(p1 == p2)


# # 运算符相关魔法方法
# # 具体咋实现自己写 是other 还是other，age
# # print(p1 > p3)   #调用__gt__方法（self，other)不写报错
# # print(p1 >=p3)   #调用__ge__方法（self，other)不写报错
# # print(p1+p2)     #调用__add__方法
# # print(p1-p2)     #调用__sub__方法
# # print(p1*p2)     #调用__mul__方法
# # __truediv__ 除法   __mod__ 取模     __power__ 幂运算


# # 类型转换魔法方法
# # __str__类型转换，打印对象都会调用str(),print()
# # __int__类型转换  int(20)
# # __float__类型转换  float()


# # 内置属性
# class Person():
#     def __init__(self, name, age):
#         self.name = name
#         self.age = age
#     def eat():
#         print(self.name+'吃饭ing')
#     def __setitem__(self,key,value):
#         # self.key=value  错误会生成一个新key
#         self.__dict__[key]=value
#     def __getitem__(self,key):
#         return self.__dict__[key]

# p1=Person('zhangsan',18)
# # print(dir(p))
# # _class__ 显示类
# # __module__ 查看模块名
# # '__dict__'  把对象属性和值转成字典
# # __dir__  （等价于dir（p))
# # __doc__  看‘’‘注释说明的’‘’
# # __slots__ 限定属性


# '''
# ['__class__', '__delattr__', '__dict__', '__dir__',
# '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
# '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
# '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
# '__weakref__', 'age', 'eat', 'name']
# '''

# # 把对象当成字典使用
# # print(p1.__dict__)  #{'name': 'zhangsan', 'age': 18}  但是不能把一个对象当字典使用

# p1['name']='lisi'   #[]语法会调用对象的__setitem__方法 需要重写__selitem__
# p1['age']=33
# print(p1.name,p1.age)  #lisi 33

# print(p1['name'])    #会调用__getitem__  lisi
# print(p1['age'])     #会调用__getitem__  33


# 对象属性与可属性

# class Student(object):  # 关注类有哪些属性和行为
#     # __slots__ = ('name','age','height') #規定可以存在的屬性
#     type1='任何哦'   #类属性 定义在类里，但是不在函数里，叫做类属性  可以类里获取，可以实例对象获取 实例对象里其实没有但是回溯找到
#                     #实例对象无法修改类属性，只能通过类对象来改，如果在实例对象中修改那就是对实例对象增加了一个实例对象属性
#                     #优先找自己的 没有才回溯类属性

#     def __init__(self, name, age):  # 在init以参数定义特征，我们称为属性
#         self.name = name
#         self.age = age
# p1=Student('zhangsan',18)
# p2=Student('lisi',20)
# print('ox%x,ox%x',(id(p1),id(p2)))  #实例对象 只要创建了就有自己的属性的实例对象
# #p1 与 p2 都是通过student 类创建出来的那么student类在哪
# print('ox%x',id(Student))    #类对象
# print(p1.name) #实例对象属性 每个实例对象都单独保存的属性，没有关联互不影响
# print(Student.type1='lll')   #修改类属性


# 伪私有变量  __(双下划线)
# 私有变量不能被外部调用
# import datetime
# class Person():
#     def __init__(self, name, age, __money):
#         self.name = name
#         self.age = age
#         self.__money = __money

#     def get_money(self):  # 内部接口获取
#         print('{}查询余额'.format(datetime.datetime.now()))
#         print('您正在查询余额')
#         return self.__money

#         #内部接口的意义在于可以记录
#     def set_mony(self,key):
#         if type(key) ==int:
#             key*=1.00
#         if type(key) != float:
#             print('修改失，只能用数字')
#             return
#         else:
#             self.__money=key

#     def __test(self):
#         print('我是私有函数，外部无法调用') #我是私有函数，外部无法调用，可以使用_对象__私有方法名调用或者通过接口调用


# p1 = Person('zhangsan', 18, 99)
# # print(p1.age)
# # print(p1.__money)  #不能直接获取私有变量
# # print(p1._Person__money)  # 通过_对象__私有变量名来获取私有属性
# # print(p1.get_money())  # 通过内部接口获取
# # propery获取
# print(p1.set_mony(2233))
# print(p1.get_money())


# # 类方法 静态方法

# class Person():
#     type_1 = '类属性'

#     def __init__(self, name, age):
#         self.name = name
#         self.age = age

#     def eat(self, food):
#         print(self.name+'正在再吃'+food)

#     # 荣誉感一个方法里没有用到实例对象的任何属性，可以吧这个方法成static
#     @staticmethod  # 静态方法
#     def demo(a, b):
#         print('staticmethod #静态方法演示    ' + str(a+b))
#     # 使用类.静态方法()直接使用 Person.demo()

#     @classmethod  # 类方法
#     def test(cls):
#         # 类方法里的cls也不用手动传参，可以自动传参
#         # cls指的是类对象 cls is Person
#         print(cls.type_1)
#         print('classmethod  #类方法')


# # 实例对象调用
# p1 = Person('张三', 18)  # 生成实例对象
# p1.eat('面条')  # 直接使用实例对象调用方法

# # 类对象调用 不会自动传参self
# # Person.eat('大米') #报错 TypeError  不会自动传参数self  : eat() missing 1 required positional argument: 'food'
# # Person.eat(p1,'大米') #手动传入p1  类对象调用
# # Person.demo(5,1) #静态方法调用


# # 类方法调用
# # 实例对象调用
# p1.test()
# # Person.test()


# 单例设计模式
# class Singleton():
#     __instance = None
#     __xxx = True

#     @classmethod
#     def __new__(cls, *args, **kwargs):
#         if cls.__instance is None:
#             # 申请内存 创建对象 并把类型设置为cls
#             cls.__instance = object.__new__(cls)
#         return cls.__instance

#     def __init__(self, a, b):
#         self.a = a
#         self.b = b

#     # 这里这样写的话就可以做到只让写一次 以后因为if不成立不执行
#     # def __init__(self, a, b):
#     #     if self.__xxx:
#     #         self.a = a
#     #         self.b = b
#     #         self.__xxx = False


# p1 = Singleton(1, 2)
# p2 = Singleton(11, 12)

# print(p1 is p2)  # 注意这样的话后面的p2的数据会重新写，p1=p2 里面的数据是最后一个写入的
# print(p1.a, p2.a)



#_____________________________________________________

# # 继承的使用
# class Animail():
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age

#     def __drink (self):
#         print('喝水')
#     def sleep(self):
#         print(self.name+'睡觉中')


# class Dog():
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age
#     def sleep(self):
#         print(self.name+'睡觉中')
#     def bark(self):
#         print(self.name+'叫')

# class Dog(Animail):   #()中是父类
#         def bark(self):
#             print(self.name+'叫')

# class Student(Animail):
#     def work(self):
#         print(self.name+'工作中')
#     def eat(self):
#         print(self.name+'吃饭中')
#     def study(self):
#         print(self.name+'学习中')

# d1=Dog('大黄',18)
# print(d1.name)
# d1.sleep()
# # d1.eat()
# s1=Student('小米',8)
# s1.eat()
# p1._A
# # 继承特点 运行多继承  如果多个父类里有相投的函数，调用写在前面的父类  继承可以传递
# # 优先查找前面的父类里的 朔及过往

# class People(Dog,Student):
#     def write(self):
#         print('write')
# p1=People('xi',20)

# p1.bark()
# p1.eat()

# 继承的私有属性特征  父类的私有方法不会继承 
# 对象名._类名__对象名可以调用  类名需要用父类的 可以强制使用


#__________________新式类与经典类___________________(py2里的概念)

# 新式类 继承自object的类
# 经典类 不继承自object的类
# 这个python2的区别 3里没区别

#__________________面向对象相关方法____________________
# class Animail():
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age

# p1 =Animail('张三',15)
# p2 =Animail('张三',15)
# class X():  #父对象
#     pass
# class Student(Animail):  #父对象
#     pass
# s1 = Student('小王',15)
# print(type(p1)) #获取类对象Animail:
# print(type(s1))   #Student 但是不能溯及父对象
# print(isinstance(s1,(Animail,X)))   #isinstance 判断一个对象是不是制定的类(父类)实例化出来的  or关系  True
# print(isinstance(s1,Student))   #isinstance 判断一个对象是不是制定的类(父类)实例化出来的  True
# # print(p1 is p2)  #是不是同一个对象
# # issubclass 前面的是不是后面的子类
# print(issubclass(Student,(Animail,X))) #前面的是不是后面的子类 OR关系  True
# print(issubclass(Student,X))           #前面的是不是后面的子类  false


###__________________子类重写父类方法__________________

# 当子类与父类方法不一样时 重写同名方法即可
# 子类在父类的基础上又更多实现时 在父类上添加功能
# 调用父类方法的两种方式
# 1 Animail.__方法名__(self,参数列表)  手动调用
# 2 使用 super(Anmail,self).__init__(name,age)  (推荐)
# class Animail():
#     def __init__(self,name,age):
#         self.name=name
#         self.age=age
#     def eat(self):
#         print('吃饭')

# class Student(Animail):
#     def __init__(self,name,age,school):
#         # Animail.__init__(self,name,age)
#         super().__init__(name,age)   #注意super()后面的()
#         self.school = school
#     def eat(self):
#         print('吃东西')
    
# s1=Student('xiao',16,'ww')
# s1.eat()
# Animail.eat(s1)
# print(s1.school)


###__________________多态__________________
# 基于继承的,通过子类重写弗雷芳芳
# 达到不同的子类对象调用相同的父类方法,得到不同的结果
# 提高代码的灵活性

# class Person():
#     def __init__(self,name):
# #         self.name=name
        
#     def work(self):
#         print(self.name+'正在指挥警犬')
#         self.dog.attack()

#     def work1(self):
#         print(self.name+'正在指挥警犬')
#         self.dog.bingd()

# # class Policedog():
# #     def attack(self):
# #         print('警犬在追毒贩')

# class blinddog():
#     def bingd():
#         print('导盲中')

# class Drugdog():
#     print('查毒中')

# pd =Policedog()          #先定义狗
# police=Person('王警官')  #再定义人
# police.dog = pd          #把狗给人
# police.work()            #调用狗方法


#######_______多态________写法

#不需要考虑狗的类型调用了
#直接调用狗了不用考虑各种狗的调用
class Person():
    def __init__(self,name):
        self.name=name
        self.dog = None
        
    def work_with_dog(self):
        if self.dog is not None and isinstance(self.dog,Dog):
            self.dog.work()

class Dog():
    def work(self):
        print('汪汪队待命中')  

class Policedog(Dog):
    def work(self):
        print('警犬在追毒贩')
    # pass

class Blinddog(Dog):
    def work(self):
        print('导盲中')
    # pass

class Drugdog(Dog):
    # def work(self):
    #     print('查毒中')
    pass


p=Person('小王')

dd=Policedog()
p.dog=dd
p.work_with_dog()

d1=Blinddog()
p.dog=d1
p.work_with_dog()

d2=Drugdog()
p.dog=d2
p.work_with_dog()